using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using OpenTK;

namespace SM64DSe
{
    class KCL
    {
        /*
         * old KCL code copypasted from SM64DSe v1.01
         * meant for debugging
         * and not likely to have any other use
         */

        public KCL(NitroFile file)
        {
            m_File = file;

	        m_Planes = new List<Plane>();

	        m_PointsSectionOffset = m_File.Read32(0x00);
	        m_NormalsSectionOffset = m_File.Read32(0x04);
	        m_PlanesSectionOffset = m_File.Read32(0x08);
	        m_OctreeSectionOffset = m_File.Read32(0x0C);

	        for (uint offset = m_PlanesSectionOffset + 0x10; offset < m_OctreeSectionOffset; offset += 0x10)
	        {
		        Plane plane = new Plane();

		        int length = (int)m_File.Read32(offset);

		        ushort pt_id = m_File.Read16(offset + 0x04);
		        int pt_x = (int)m_File.Read32((uint)(m_PointsSectionOffset + (pt_id*12)    ));
		        int pt_y = (int)m_File.Read32((uint)(m_PointsSectionOffset + (pt_id*12) + 4));
		        int pt_z = (int)m_File.Read32((uint)(m_PointsSectionOffset + (pt_id*12) + 8));

		        ushort nr_id = m_File.Read16(offset + 0x06);
		        short nr_x = (short)m_File.Read16((uint)(m_NormalsSectionOffset + (nr_id*6)    ));
		        short nr_y = (short)m_File.Read16((uint)(m_NormalsSectionOffset + (nr_id*6) + 2));
		        short nr_z = (short)m_File.Read16((uint)(m_NormalsSectionOffset + (nr_id*6) + 4));

		        ushort d1_id = m_File.Read16(offset + 0x08);
		        short d1_x = (short)m_File.Read16((uint)(m_NormalsSectionOffset + (d1_id*6)    ));
                short d1_y = (short)m_File.Read16((uint)(m_NormalsSectionOffset + (d1_id*6) + 2));
		        short d1_z = (short)m_File.Read16((uint)(m_NormalsSectionOffset + (d1_id*6) + 4));

		        ushort d2_id = m_File.Read16(offset + 0x0A);
		        short d2_x = (short)m_File.Read16((uint)(m_NormalsSectionOffset + (d2_id*6)    ));
		        short d2_y = (short)m_File.Read16((uint)(m_NormalsSectionOffset + (d2_id*6) + 2));
		        short d2_z = (short)m_File.Read16((uint)(m_NormalsSectionOffset + (d2_id*6) + 4));

		        ushort d3_id = m_File.Read16(offset + 0x0C);
		        short d3_x = (short)m_File.Read16((uint)(m_NormalsSectionOffset + (d3_id*6)    ));
		        short d3_y = (short)m_File.Read16((uint)(m_NormalsSectionOffset + (d3_id*6) + 2));
		        short d3_z = (short)m_File.Read16((uint)(m_NormalsSectionOffset + (d3_id*6) + 4));

		        plane.m_Length = (float)length / 65536000f;
		        plane.m_Position = new Vector3((float)pt_x / 64000f, (float)pt_y / 64000f, (float)pt_z / 64000f);
		        plane.m_Normal = new Vector3((float)nr_x / 1024f, (float)nr_y / 1024f, (float)nr_z / 1024f);
		        plane.m_Dir1 = new Vector3((float)d1_x / 1024f, (float)d1_y / 1024f, (float)d1_z / 1024f);
		        plane.m_Dir2 = new Vector3((float)d2_x / 1024f, (float)d2_y / 1024f, (float)d2_z / 1024f);
		        plane.m_Dir3 = new Vector3((float)d3_x / 1024f, (float)d3_y / 1024f, (float)d3_z / 1024f);
		        plane.m_TerrainType = m_File.Read16(offset + 0x0E);

               /* if (Math.Abs(plane.m_Dir1.Length - 1f) > 0.0001f || Math.Abs(plane.m_Dir2.Length - 1f) > 0.0001f ||
                    Math.Abs(plane.m_Dir3.Length - 1f) > 0.0001f || Math.Abs(plane.m_Normal.Length - 1f) > 0.0001f || 
                    plane.m_Length < 0f)
                    MessageBox.Show(string.Format("WRONG PLANE | {0} | {1} | {2} | {3} | {4}",
                        plane.m_Dir1.Length, plane.m_Dir2.Length, plane.m_Dir3.Length, plane.m_Normal.Length, plane.m_Length));

                if (plane.m_Dir1.Length < 0.001f || plane.m_Dir2.Length < 0.001f ||
                    plane.m_Dir3.Length < 0.001f || plane.m_Normal.Length < 0.001f ||
                    Math.Abs(plane.m_Length) < 0.001f)
                    MessageBox.Show(string.Format("ZERO PLANE | {0} | {1} | {2} | {3} | {4}",
                        plane.m_Dir1.Length, plane.m_Dir2.Length, plane.m_Dir3.Length, plane.m_Normal.Length, plane.m_Length));

                Vector3 lol1 = Vector3.Cross(plane.m_Dir1, plane.m_Normal);
                float lol1len = plane.m_Length / (float)Math.Cos(Math.Acos(Math.Min(1f,Vector3.Dot(lol1, plane.m_Dir3))));
                Vector3 lol2 = Vector3.Cross(plane.m_Normal, plane.m_Dir2);
                float lol2len = plane.m_Length / (float)Math.Cos(Math.Acos(Math.Min(1f,Vector3.Dot(lol2, plane.m_Dir3))));
                if (Helper.VectorsEqual(plane.m_Position, Vector3.Multiply(lol1, lol1len)) ||
                    Helper.VectorsEqual(plane.m_Position, Vector3.Multiply(lol2, lol2len)))
                    MessageBox.Show(string.Format("WEIRD PLANE {5:X8} | {0} | {1} | {2} | {3} | {4}\n{6} | {7} / cos(acos({8})) = cos({9}) = {10}",
                        plane.m_Dir1, plane.m_Dir2, plane.m_Dir3, plane.m_Normal, plane.m_Length,
                        offset, lol2, plane.m_Length, Vector3.Dot(lol2, plane.m_Dir3), Math.Acos(Vector3.Dot(lol2, plane.m_Dir3)), Math.Cos(Math.Acos(Vector3.Dot(lol2, plane.m_Dir3)))));
                */
		        m_Planes.Add(plane);
	        }

            int shift = (int)m_File.Read32(0x2C);
            Vector3 octreestart = new Vector3((float)(int)m_File.Read32(0x14) / 64000f, (float)(int)m_File.Read32(0x18) / 64000f, (float)(int)m_File.Read32(0x1C) / 64000f);
            float _cubesize = (float)(1 << shift) / 1024f;
            Vector3 cubesize = new Vector3(_cubesize, _cubesize, _cubesize);
            Vector3 octreesize = new Vector3((~m_File.Read32(0x20) >> shift) + 1, (~m_File.Read32(0x24) >> shift) + 1, (~m_File.Read32(0x28) >> shift) + 1);
            OctreeNode.m_List = new List<OctreeNode>();
            uint loloffset = m_OctreeSectionOffset;
            for (int z = 0; z < octreesize.Z; z++)
                for (int y = 0; y < octreesize.Y; y++)
                    for (int x = 0; x < octreesize.X; x++)
                    { new OctreeNode(m_File, m_OctreeSectionOffset, loloffset, octreestart + new Vector3(cubesize.X * x, cubesize.Y * y, cubesize.Z * z), cubesize); loloffset += 4; }

            //MessageBox.Show(OctreeNode.m_List.Count.ToString());
        }


        public class Plane
	    {
		    public Vector3 m_Position;
            public float m_Length;
            public Vector3 m_Normal;
            public Vector3 m_Dir1;
            public Vector3 m_Dir2;
            public Vector3 m_Dir3;
            public ushort m_TerrainType;
	    }

        public class OctreeNode
        {
            public static List<OctreeNode> m_List;

            public Vector3 m_Pos, m_Size;
            public int m_NumPlanes;
            public bool m_LOL;

            public OctreeNode(NitroFile file, uint baseoffset, uint offset, Vector3 pos, Vector3 size)
            {
                m_Pos = pos;
                m_Size = size;
                m_LOL = false;

                uint node = file.Read32(offset);
                if ((node & 0x80000000) != 0)
                {
                    uint lolz = baseoffset + (node & 0x7FFFFFFF) + 2;
                    int n = 0;
                    string lmao = "";
                    for (; ; )
                    {
                        ushort p = file.Read16(lolz);
                        if (p == 0)
                            break;
                        else if (p == 37)
                            m_LOL = true;

                        lmao += (p - 1).ToString() + " ";
                        lolz += 2;
                        n++;
                    }

                    //if (m_LOL)
                     //   MessageBox.Show(lmao);

                    m_NumPlanes = n;
                    OctreeNode.m_List.Add(this);
                }
                else
                {
                    uint parentoffset = baseoffset + node;
                    uint child0offset = parentoffset;
                    size /= 2f;
                    for (int z = 0; z < 2; z++)
                        for (int y = 0; y < 2; y++)
                            for (int x = 0; x < 2; x++)
                            { new OctreeNode(file, child0offset, parentoffset, pos + new Vector3(size.X * x, size.Y * y, size.Z * z), size); parentoffset += 4; }
                }
            }
        }

        private NitroFile m_File;
        public List<Plane> m_Planes;

        uint m_PointsSectionOffset;
        uint m_NormalsSectionOffset;
        uint m_PlanesSectionOffset;
        uint m_OctreeSectionOffset;
    }
}
